package quicklunch.e1.goodies.utils;

import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

/**
 * @author nakawaka
 */
public abstract class DOMUtils {

    /**
     * @param parent
     * @param name
     * @return
     */
    public static Element createChildElement(Element parent, String name) {
        Document doc = parent.getOwnerDocument();
        Element element = doc.createElement(name);
        parent.appendChild(element);
        return element;
    }

    /**
     * @param s
     * @return
     */
    public static String encodeAttrQuot(final String s) {
        if (s == null) {
            return null;
        }
        char[] content = s.toCharArray();
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
            case '<':
                buf.append("&lt;");
                break;
            case '>':
                buf.append("&gt;");
                break;
            case '&':
                buf.append("&amp;");
                break;
            case '"':
                buf.append("&quot;");
                break;
            default:
                buf.append(content[i]);
            }
        }
        return buf.toString();
    }

    /**
     * @param s
     * @return
     */
    public static String encodeText(final String s) {
        if (s == null) {
            return null;
        }
        char[] content = s.toCharArray();
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
            case '<':
                buf.append("&lt;");
                break;
            case '>':
                buf.append("&gt;");
                break;
            case '&':
                buf.append("&amp;");
                break;
            default:
                buf.append(content[i]);
            }
        }
        return buf.toString();
    }

    /**
     * @param document
     * @return
     */
    public static String toString(Document document) {
        StringBuffer buf = new StringBuffer();
        appendElement(document.getDocumentElement(), buf);
        return buf.toString();
    }

    /**
     * @param element -
     * @return String
     */
    public static String toString(Element element) {
        if (element == null)
            return null;
        StringBuffer buf = new StringBuffer();
        appendElement(element, buf);
        return buf.toString();
    }

    /**
     * @param element
     * @param buf
     */
    public static void appendElement(Element element, StringBuffer buf) {
        String tag = element.getTagName();
        buf.append('<');
        buf.append(tag);
        appendAttrs(element.getAttributes(), buf);
        buf.append('>');
        appendChildren(element.getChildNodes(), buf);
        buf.append("</");
        buf.append(tag);
        buf.append('>');
    }

    /**
     * @param children
     * @param buf
     */
    public static void appendChildren(NodeList children, StringBuffer buf) {
        int length = children.getLength();
        for (int i = 0; i < length; ++i) {
            appendNode(children.item(i), buf);
        }
    }

    /**
     * @param attrs
     * @param buf
     */
    public static void appendAttrs(NamedNodeMap attrs, StringBuffer buf) {
        int length = attrs.getLength();
        for (int i = 0; i < length; ++i) {
            Attr attr = (Attr) attrs.item(i);
            buf.append(' ');
            appendAttr(attr, buf);
        }
    }

    /**
     * @param attr
     * @param buf
     */
    public static void appendAttr(Attr attr, StringBuffer buf) {
        buf.append(attr.getName());
        buf.append("=\"");
        buf.append(encodeAttrQuot(attr.getValue()));
        buf.append('\"');
    }

    /**
     * @param text
     * @param buf
     */
    public static void appendText(Text text, StringBuffer buf) {
        buf.append(encodeText(text.getData()));
    }

    public static void appendComment(Comment comment, StringBuffer buf) {
        buf.append("<!--");
        buf.append(comment.getData());
        buf.append("-->");
    }

    /**
     * @param cdataSection
     * @param buf
     */
    public static void appendCDATASection(CDATASection cdataSection, StringBuffer buf) {
        buf.append("<![CDATA[");
        buf.append(cdataSection.getData());
        buf.append("]]>");
    }

    /**
     * @param entityReference
     * @param buf
     */
    public static void appendEntityReference(EntityReference entityReference, StringBuffer buf) {
        buf.append('&');
        buf.append(entityReference.getNodeName());
        buf.append(';');
    }

    /**
     * @param node
     * @param buf
     */
    public static void appendNode(Node node, StringBuffer buf) {

        switch (node.getNodeType()) {

        case Node.ELEMENT_NODE:
            appendElement((Element) node, buf);
            break;
        case Node.TEXT_NODE:
            appendText((Text) node, buf);
            break;
        case Node.CDATA_SECTION_NODE:
            appendCDATASection((CDATASection) node, buf);
            break;
        case Node.ENTITY_REFERENCE_NODE:
            appendEntityReference((EntityReference) node, buf);
            break;
        case Node.COMMENT_NODE:
            appendComment((Comment) node, buf);
            break;
        case Node.ATTRIBUTE_NODE:
            ;
        case Node.ENTITY_NODE:
            ;
        case Node.PROCESSING_INSTRUCTION_NODE:
            ;
        case Node.DOCUMENT_NODE:
            ;
        case Node.DOCUMENT_TYPE_NODE:
            ;
        case Node.DOCUMENT_FRAGMENT_NODE:
            ;
        case Node.NOTATION_NODE:
            ;
        default:

            ;
        }
    }
}
